home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 101-125 / disk_107 / diff / diff.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  25KB  |  936 lines

  1. /*
  2.     Name:           Diff.c
  3.     Processor:      VAX | MS-DOS
  4.     Class:          C Program
  5.     Creation Date:  1/8/87
  6.     Revision:
  7.     Author:         D. Krantz
  8.  
  9.     Description:    File compare and change-bar for text files.
  10. */
  11.  
  12. /*
  13.     Source:         Dr. Dobb's Journal of Software Tools
  14.     Issue:          #130 August 1987
  15.     Article:        UTILITIES:  What's the DIFF?  Page 30.
  16.     PD Validation:  Page 39, Column 1, Paragraph 6
  17.                     "I am releasing DIFF into the public domain..."
  18.  
  19.     Amiga Port:     Jeffrey Bailey
  20.     Revision Date:  10/5/87
  21.  
  22.     Major Changes:  All internal tracing and debugging routines removed.
  23.                     Memory allocation tracking routines added.
  24.                     CTRL-C trapping added.
  25.                     Minor bugs fixed.
  26.                     Linked list routines partially re-written to allow better
  27.                         usage of memory with a tradeoff in performance.
  28.                         (storage for lines is dynamically allocated to the
  29.                         exact size needed)
  30.                     Added /SOURCE_CODE option.
  31.                     Added /TRIM & /NOTRIM options.
  32.                     Changed /UP_CASE to /NOEXACT for better VMS compatibility.
  33.                     Added register declarations for optimization purposes.
  34.  
  35.     Known Bugs and Problems:
  36.  
  37.                     Diff looks for options to start with a forward slash '/'.
  38.                     If a file spec starts with a forward slash, Diff will
  39.                     assume it to be an option and will probably generate an
  40.                     error message.  Two possible solutions:
  41.  
  42.                         o  Always specify file names in such a way that no
  43.                            leading slashes occur.
  44.  
  45.                         o  Change OPT_FLAG to another character and re-compile,
  46.                            losing VMS command line "look and feel".
  47.  
  48.                     Options may be abbreviated.  For example /SOURCE_CODE may
  49.                     be abbreviated as /SOURCE.  However, if you do not specify
  50.                     enough characters, the option may be ambiguous.  If this
  51.                     happens, Diff will not issue a warning; the parser will
  52.                     accept this situation on a first come, first served basis.
  53.                     This was a problem from the original version.  I just didn't
  54.                     think it was worth the effort to fix it.
  55.  
  56.                     Polling for CTRL-C adds overhead.  I just didn't know of a
  57.                     better way to do it.  Also, instead of the standard "^C",
  58.                     this program prints " Interrupt " in reverse video to
  59.                     maintain the VMS "look and feel".  Feel free to change it.
  60.  
  61.                     The memory allocation tracking routines add even more
  62.                     overhead.  However, they are necessary because the author
  63.                     of this program was sloppy about resource tracking (VMS
  64.                     does it for you, and with MS-DOS it's not a concern).
  65.                     I couldn't use the system routines AllocRemember() and
  66.                     FreeRemember() because this program frees individual
  67.                     chunks of memory at different points in time.
  68.                     FreeRemember() is an all or nothing process -- unsuitable
  69.                     for this program.  (If I'm incorrect about this, please
  70.                     feel free to change the source.  This is my first major
  71.                     project on the Amiga and I'm not totally familiar with
  72.                     the OS yet.)
  73. */
  74. #include <stdio.h>
  75. #include <ctype.h>
  76.  
  77. /*
  78.     All of these #include's are probably not necessary, but the C compiler
  79.     always seems to throw a fit if they aren't in the right order or something.
  80. */
  81. #include "exec/types.h"
  82. #include "exec/nodes.h"
  83. #include "exec/lists.h"
  84. #include "exec/ports.h"
  85. #include "exec/interrupts.h"
  86. #include "exec/io.h"
  87. #include "exec/memory.h"
  88. #include "libraries/dos.h"
  89. #include "libraries/dosextens.h"
  90.  
  91. /*
  92.     Declaration for my memory routine.
  93. */
  94. char *TrackMalloc();
  95.  
  96. /*
  97.     Specifies the flag character.
  98. */
  99. #define OPT_FLAG '/'
  100.  
  101. #define MAXLINE 2048
  102.  
  103. #define FORMFEED '\014'
  104.  
  105. struct LINE {
  106.             unsigned long linenum;
  107.             unsigned long pagenum;
  108.             struct LINE *link;
  109.             char *text;
  110.             char *dup;
  111.             };
  112.  
  113. typedef struct LINE *line_ptr;
  114.  
  115. typedef char *char_ptr;
  116.  
  117. typedef FILE *FILE_PTR;
  118.  
  119. struct LINE root [3];
  120.  
  121. FILE_PTR msg;
  122.  
  123. unsigned long line_count [3] = {1, 1, 1};
  124. unsigned long page_count [3] = {1, 1, 1};
  125.  
  126. int command_errors = 0;
  127. char xx1 [132], xx2 [132];
  128. int files = 0;
  129. char_ptr infile_name [3] = {NULL, xx1, xx2};
  130. char outfile_name [132];
  131. FILE_PTR infile [3];
  132. FILE *outfile;
  133. static line_ptr at [3] = {NULL, &(root [1]), &(root [2])};
  134.  
  135. int debug = 0;
  136. int trace_enabled = 0;
  137. int bar_col = 0;
  138. unsigned long top_skip = 0;
  139. unsigned long bot_skip = 0;
  140. unsigned long page_len = 66;
  141. int up_case = 0;
  142. int re_sync = 5;
  143. int output = 0;
  144. int blanks = 0;
  145. int lookahead = 200;
  146. int skip1 = 0;
  147. int skip2 = 0;
  148. int trim_lines = 0;
  149.  
  150. main(argc, argv)
  151.     int argc;
  152.     char *argv [];
  153.     {
  154.     int  i;
  155.  
  156.     /* Initialize our file pointers so cleanup() only closes open files. */
  157.     i = 0;
  158.     while(i < 3)
  159.         {
  160.         infile [i] = NULL;
  161.         i++;
  162.         }
  163.     outfile = NULL;
  164.     if(argc == 1) help();
  165.     msg = stdout;
  166.     for(i = 1; i < argc; i++)  strip_opt(argv [i]);
  167.     if(files < 2)
  168.         {
  169.         printf("DIFF:  Error:  Must specify two files.\n");
  170.         cleanup();
  171.         }
  172.     open_files();
  173.     if(command_errors)
  174.         cleanup();
  175.     page_skip();
  176.     diff();
  177.     cleanup();
  178.     }
  179.  
  180. dont_look(line)
  181.     register line_ptr line;
  182.     {
  183.     register char *temp;
  184.  
  185.  
  186.     check_ctrl_c();
  187.     if(line == NULL) return(0);
  188.     if(line->linenum <= top_skip) return(1);
  189.     if(line->linenum > page_len - bot_skip) return(1);
  190.     temp = line->text;
  191.     if(!blanks)
  192.         {
  193.         while(*temp != 0)
  194.             {
  195.             switch(*temp)
  196.                 {
  197.                 case ' ':
  198.                 case '\t':
  199.                 case '\n':
  200.                     break;
  201.                 default :
  202.                     return(0);
  203.                     break;
  204.                 }
  205.             temp++;
  206.             }
  207.         return(1);
  208.         }
  209.     return(0);
  210.     }
  211.  
  212. equal(a, b)
  213.     line_ptr a, b;
  214.     {
  215.     check_ctrl_c();
  216.     if( (a == NULL) || (b == NULL) ) return(0);
  217.     if(up_case) return(!strcmp(a->dup, b->dup));
  218.         else return(!strcmp(a->text, b->text));
  219.     }
  220.  
  221. position(f, where)
  222.     int  f;
  223.     line_ptr where;
  224.     {
  225.     check_ctrl_c();
  226.     at [f] = &root [f];
  227.     while(at [f]->link != where)
  228.         at[f] = at [f]->link;
  229.     return(0);
  230.     }
  231.  
  232. char *index(str, c)
  233.     register char *str, c;
  234.     {
  235.     check_ctrl_c();
  236.     while(*str != NULL)
  237.         {
  238.         if(*str == c) return(str);
  239.         str++;
  240.         }
  241.     return(NULL);
  242.     }
  243.  
  244. line_ptr next_line(f)
  245.     int  f;
  246.     {
  247.     line_ptr place_hold;
  248.     char temp [MAXLINE + 1];
  249.  
  250.     check_ctrl_c();
  251.     if( at [f]->link != NULL)
  252.         {
  253.         at [f] = at [f]->link;
  254.         return(at [f]);
  255.         }
  256.     else
  257.         {
  258.         at [f]->link = (line_ptr) TrackMalloc(sizeof(struct LINE));
  259.         if(at [f]->link == NULL)
  260.             {
  261.             printf("DIFF:  Out of Memory.\n");
  262.             cleanup();
  263.             }
  264.         place_hold = at [f];
  265.         at [f] = at [f]->link;
  266.         at [f]->link = NULL;
  267.         at [f]->text = NULL;
  268.         at [f]->dup  = NULL;
  269.         if(fgets(temp, MAXLINE, infile [f]) == NULL)
  270.             {
  271.             TrackFree(at [f]);
  272.             at [f] = place_hold;
  273.             at [f]->link = NULL;
  274.             return(NULL);
  275.             }
  276.         if(trim_lines)
  277.             {
  278.             trim(temp);
  279.             strcat(temp, "\n");
  280.             }
  281.         at [f]->text = TrackMalloc(strlen(temp) + 1);
  282.         if(at [f]->text == NULL)
  283.             {
  284.             printf("DIFF:  Out of Memory.\n");
  285.             cleanup();
  286.             }
  287.         strcpy(at [f]->text, temp);
  288.         if( (index( at [f]->text, FORMFEED) != NULL) ||
  289.             (line_count [f] > page_len))
  290.             {
  291.             page_count [f]++;
  292.             line_count [f] = 1;
  293.             }
  294.         at [f]->linenum = line_count [f]++;
  295.         at [f]->pagenum = page_count [f];
  296.         if(up_case)
  297.             {
  298.             at [f]->dup = TrackMalloc(strlen(at [f]->text) + 1);
  299.             if(at [f]->dup == NULL)
  300.                 {
  301.                 printf("DIFF:  Out of Memory.\n");
  302.                 cleanup();
  303.                 }
  304.             strcpy(at [f]->dup, at [f]->text);
  305.             upper(at [f]->dup);
  306.             }
  307.         return(at [f]);
  308.         }
  309.     }
  310.  
  311. discard(f, to)
  312.     int  f;
  313.     line_ptr to;
  314.     {
  315.     register line_ptr temp;
  316.  
  317.     check_ctrl_c();
  318.     while(1)
  319.         {
  320.         if(root [f].link == NULL) break;
  321.         temp = root [f].link;
  322.         root [f].link = root [f].link->link;
  323.         TrackFree(temp->text);
  324.         if(temp->dup != NULL) TrackFree(temp->dup);
  325.         TrackFree(temp);
  326.         if(temp == to) break;
  327.         }
  328.     at [f] = &root [f];
  329.     return(0);
  330.     }
  331.  
  332. vfputs(str, file)
  333.     char *str;
  334.     FILE *file;
  335.     {
  336.     check_ctrl_c();
  337.     fputs(str, file);
  338.     return(0);
  339.     }
  340.  
  341. put(line)
  342.     line_ptr line;
  343.     {
  344.     register line_ptr temp;
  345.  
  346.     if(output)
  347.         for(temp = root [1].link; ;)
  348.             {
  349.             check_ctrl_c();
  350.             if(temp == NULL) return(0);
  351.             vfputs(temp->text, outfile);
  352.             if(temp == line) return(0);
  353.             temp = temp->link;
  354.             }
  355.     return(0);
  356.     }
  357.  
  358. char *change_bar(str)
  359.     register char *str;
  360.     {
  361.     register int  i;
  362.     char temp [MAXLINE + 1], *base;
  363.  
  364.     check_ctrl_c();
  365.     base = temp;
  366.     strcpy(temp, str);
  367.     i = 0;
  368.     if(bar_col != 0)
  369.         {
  370.         while(*base != '\n')
  371.             {
  372.             if((*base == '\r') && (*(base + 1) != '\n')) i = 0;
  373.             base++;
  374.             i++;
  375.             }
  376.         while(i++ < bar_col) *(base)++ = ' ';
  377.         strcpy(base, "|\n");
  378.         }
  379.     else
  380.         {
  381.         if(temp [0] != ' ')
  382.             {
  383.             strcpy(&temp [1], str);
  384.             }
  385.         temp [0] = '|';
  386.         }
  387.     TrackFree(str);
  388.     base = TrackMalloc(strlen(temp) + 1);
  389.     if(base == NULL)
  390.         {
  391.         printf("DIFF:  Out of Memory.\n");
  392.         cleanup();
  393.         }
  394.     strcpy(base, temp);
  395.     return(base);
  396.     }
  397.  
  398. added(line)
  399.     line_ptr line;
  400.     {
  401.     register line_ptr temp;
  402.  
  403.     check_ctrl_c();
  404.     for(temp = root [1].link; ;)
  405.         {
  406.         if(temp == NULL) return(0);
  407.         if(!dont_look(temp))
  408.             fprintf(msg, "+ Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
  409.             temp->text);
  410.         if(output)
  411.             if(dont_look(temp)) vfputs(temp->text, outfile);
  412.             else vfputs((temp->text = change_bar(temp->text)), outfile);
  413.         if(temp == line) return(0);
  414.         temp = temp->link;
  415.         }
  416.     }
  417.  
  418. deleted(line)
  419.     line_ptr line;
  420.     {
  421.     register line_ptr temp;
  422.  
  423.     check_ctrl_c();
  424.     for(temp = root [2].link; ; )
  425.         {
  426.         if(temp == NULL) return(0);
  427.         if(!dont_look(temp))
  428.             fprintf(msg, "- Page %d : Line %d -> %s", temp->pagenum, temp->linenum,
  429.                 temp->text);
  430.         if(temp == line) return(0);
  431.         temp = temp->link;
  432.         }
  433.     return(0);
  434.     }
  435.  
  436. resync(first, second)
  437.     line_ptr first, second;
  438.     {
  439.     register line_ptr file1_start, file2_start;
  440.     line_ptr last_bad1, last_bad2, t1, t2;
  441.     int  i, j, k, moved1, moved2;
  442.  
  443.     check_ctrl_c();
  444.     moved1 = 0;
  445.     file1_start = first;
  446.  
  447.     position(1, first);
  448.     for(k = 0; k < lookahead; k++)
  449.         {
  450.         while(dont_look(file1_start = next_line(1)));
  451.         if(file1_start == NULL) goto no_sy;
  452.  
  453.         moved2 = 0;
  454.         file2_start = second;
  455.  
  456.         position(2, second);
  457.         for(j = 0; j < lookahead; j++)
  458.             {
  459.             while(dont_look(file2_start = next_line(2)));
  460.             if(file2_start == NULL) goto eof2;
  461.  
  462.             t1 = file1_start;
  463.             t2 = file2_start;
  464.             for(i = 0; (i < re_sync) && equal(t1, t2); i++)
  465.                 {
  466.                 while(dont_look(t1 = next_line(1)));
  467.                 while(dont_look(t2 = next_line(2)));
  468.                 if( (t1 == NULL) || (t2 == NULL)) break;
  469.                 }
  470.             if(i == re_sync) goto synced;
  471.  
  472.             last_bad2 = file2_start;
  473.             position(2, file2_start);
  474.             while(dont_look(file2_start = next_line(2)));
  475.             moved2++;
  476.             }
  477. eof2:
  478.         check_ctrl_c();
  479.         last_bad1 = file1_start;
  480.         position(1, file1_start);
  481.         while(dont_look(file1_start = next_line(1)));
  482.         moved1++;
  483.         }
  484.     printf("DIFF:  Lost sync in file %s at page %d line %d.\n",
  485.         infile_name [1], first->pagenum, first->linenum);
  486.     fclose(outfile);
  487.     outfile = NULL;
  488.     cleanup();
  489. no_sy:
  490.     check_ctrl_c();
  491.     position(1, first);
  492.     while( (first = next_line(1)) != NULL)
  493.         {
  494.         added(first);
  495.         discard(1, first);
  496.         }
  497.     return(0);
  498. synced:
  499.     check_ctrl_c();
  500.     if(moved1)
  501.         {
  502.         added(last_bad1);
  503.         discard(1, last_bad1);
  504.         }
  505.     position( 1, file1_start);
  506.     if(moved2)
  507.         {
  508.         deleted(last_bad2);
  509.         discard(2, last_bad2);
  510.         }
  511.     position(2, file2_start);
  512.     fprintf(msg, "\n");
  513.     return(0);
  514.     }
  515.  
  516. diff()
  517.     {
  518.     register line_ptr first, second;
  519.  
  520.     check_ctrl_c();
  521.     while(1)
  522.         {
  523.         while(dont_look(first = next_line(1)));
  524.         if(first == NULL)
  525.             {
  526.             put(first);
  527.             return(0);
  528.             }
  529.         while(dont_look(second = next_line(2)));
  530.         if(equal(first, second))
  531.             {
  532.             put(first);
  533.             discard(1, first);
  534.             discard(2, second);
  535.             }
  536.         else resync(first, second);
  537.         if(second == NULL) return(0);
  538.         }
  539.     }
  540.  
  541. page_skip()
  542.     {
  543.     register line_ptr first, second;
  544.  
  545.     check_ctrl_c();
  546.     while(1)
  547.         {
  548.         first = next_line(1);
  549.         if( (first == NULL) || (first->pagenum > skip1) ) break;
  550.         put(first);
  551.         discard(1, first);
  552.         }
  553.     if(first != NULL) position(1, first);
  554.     while(1)
  555.         {
  556.         second = next_line(2);
  557.         if( (second == NULL) || (second->pagenum > skip2) ) break;
  558.         discard(2, second);
  559.         }
  560.     if(second != NULL) position(2, second);
  561.     return(0);
  562.     }
  563.  
  564. help()
  565.     {
  566.     printf("\nDIFF");
  567.     printf("\nText File Difference Detector");
  568.     printf("\n");
  569.     printf("\nFormat:");
  570.     printf("\nDIFF [option {option} ...] newfile oldfile [barfile]");
  571.     printf("\n");
  572.     printf("\n    newfile = latest revision of text file.");
  573.     printf("\n    oldfile = baseline to compare against.");
  574.     printf("\n    barfile = output file if change bars are desired.");
  575.     printf("\n");
  576.     printf("\nOptions:");
  577.     printf("\n    /BAR_COL=n    Column of output file in which change bar");
  578.     printf("\n                  will appear.  Default = %d", bar_col);
  579.     printf("\n    /BLANKS       Blank lines are to be considered significant.");
  580.     printf("\n    /NOBLANKS     Blank lines are not significant.  DEFAULT");
  581.     printf("\n    /BOT_SKIP=n   Lines at bottom of page to skip for running");
  582.     printf("\n                  footers and page numbers.  Default = %d",
  583.         bot_skip);
  584.     printf("\n    /EXACT        Case is significant.  DEFAULT");
  585.     printf("\n    /NOEXACT      Upper/Lower case is not considered");
  586.     printf("\n                  significant in the comparison.");
  587.     printf("\n    /LOOKAHEAD=n  Maximum number of lines to look ahead in");
  588.     printf("\n                  each file to resync after a difference.");
  589.     printf("\n                  Default = %d", lookahead);
  590.     printf("\n    /OUTPUT=file  File to which differences summary is written.");
  591.     printf("\n                  Default is to stdout.");
  592.     printf("\n    /PAGE_LEN=n   Lines per page. (embedded form feeds override");
  593.     printf("\n                  this value)  Default = %d", page_len);
  594.     printf("\n    /RE_SYNC=n    Number of lines that must match before the");
  595.     printf("\n                  files are considered synced.  Default = %d",
  596.         re_sync);
  597.     printf("\n    /SKIP1=n      Pages in NEWFILE to skip before comparison");
  598.     printf("\n                  starts.  Also sets /SKIP2.  Default = %d",
  599.         skip1);
  600.     printf("\n    /SKIP2=n      Pages in OLDFILE to skip before comparison.");
  601.     printf("\n                  MUST BE AFTER /SKIP1 !  Default = %d", skip2);
  602.     printf("\n    /SOURCE_CODE  Causes DIFF to stop counting lines for page");
  603.     printf("\n                  breaks.  Everything is considered to be");
  604.     printf("\n                  on page 1.  (embedded form feeds override)");
  605.     printf("\n    /TOP_SKIP=n   Lines at top of page to skip for running");
  606.     printf("\n                  headings and page numbers.  Default = %d",
  607.         top_skip);
  608.     printf("\n    /TRIM_LINES   Trim trailing spaces, tabs, returns, and");
  609.     printf("\n                  newlines from the lines before comparison.");
  610.     printf("\n    /NOTRIM_LINES Do not trim lines before comparison.  DEFAULT");
  611.     printf("\n\n");
  612.     }
  613.  
  614. open_files()
  615.     {
  616.     int  i;
  617.  
  618.     check_ctrl_c();
  619.     for(i = 1; i < 3; i++)
  620.         if( (infile [i] = fopen(infile_name [i], "r")) == NULL)
  621.             {
  622.             printf("DIFF:  Error:  Can't open %s for READ!\n", infile_name [i]);
  623.             command_errors++;
  624.             }
  625.     if(files > 2)
  626.         if( (outfile = fopen(outfile_name, "w")) == NULL)
  627.             {
  628.             printf("DIFF:  Error:  Can't open %s for WRITE!\n", outfile_name);
  629.             command_errors++;
  630.             }
  631.     return(0);
  632.     }
  633.  
  634. redirect(str)
  635.     char *str;
  636.     {
  637.     char filename [132];
  638.     register char *ptr, *dest;
  639.  
  640.     check_ctrl_c();
  641.     dest = filename;
  642.     if( (ptr = index(str, '=') + 1) == (char *) (NULL + 1) )
  643.         {
  644.         printf("DIFF:  Error in option %s.\n", str);
  645.         command_errors++;
  646.         }
  647.     while( (*(dest++) = *(ptr++)) != '\0');
  648.     *dest = '\0';
  649.     if( (msg = fopen(filename, "w")) == NULL)
  650.         {
  651.         printf("DIFF:  Can't open %s for WRITE!\n", filename);
  652.         command_errors++;
  653.         }
  654.     return(0);
  655.     }
  656.  
  657. strip_opt(str)
  658.     char *str;
  659.     {
  660.     check_ctrl_c();
  661.     upper(str);
  662.     if( *str == OPT_FLAG)
  663.         {
  664.         if(match(str + 1, "BAR_COL"))
  665.             bar_col = num(str);
  666.         else if(match(str +1, "TOP_SKIP"))
  667.                 top_skip = num(str);
  668.         else if(match(str +1, "BOT_SKIP"))
  669.                 bot_skip = num(str);
  670.         else if(match(str + 1, "PAGE_LEN"))
  671.                 page_len = num(str);
  672.         else if(match(str + 1, "NOEXACT"))
  673.                 up_case = 1;
  674.         else if(match(str + 1, "EXACT"))
  675.                 up_case = 0;
  676.         else if(match(str + 1, "RE_SYNC"))
  677.                 re_sync = num(str);
  678.         else if(match(str + 1, "BLANKS"))
  679.                 blanks = 1;
  680.         else if(match(str + 1, "NOBLANKS"))
  681.                 blanks = 0;
  682.         else if(match(str + 1, "LOOKAHEAD"))
  683.                 lookahead = num(str);
  684.         else if(match(str + 1, "SKIP1"))
  685.                 skip1 = skip2 = num(str);
  686.         else if(match(str + 1, "SKIP2"))
  687.                 skip2 = num(str);
  688.         else if(match(str + 1, "OUTPUT"))
  689.                 redirect(str);
  690.         else if(match(str + 1, "SOURCE_CODE"))
  691.                 page_len = 2147483647L;
  692.         else if(match(str + 1, "TRIM_LINES"))
  693.                 trim_lines = 1;
  694.         else if(match(str + 1, "NOTRIM_LINES"))
  695.                 trim_lines = 0;
  696.         else
  697.             {
  698.             printf("DIFF:  Unrecognized Option:  %s.\n", str);
  699.             command_errors++;
  700.             }
  701.         }
  702.     else
  703.         {
  704.         switch(files)
  705.             {
  706.             case 0:
  707.                 strcpy(infile_name [1], str);
  708.                 break;
  709.             case 1:
  710.                 strcpy(infile_name [2], str);
  711.                 break;
  712.             case 2:
  713.                 strcpy(outfile_name, str);
  714.                 output = 1;
  715.                 break;
  716.             default :
  717.                 printf("DIFF:  Error:  Too many files specified at %s\n", str);
  718.                 command_errors++;
  719.                 break;
  720.             }
  721.         files++;
  722.         }
  723.     return(0);
  724.     }
  725.  
  726. upper(str)
  727.     register char *str;
  728.     {
  729.     check_ctrl_c();
  730.     while(*str != NULL)
  731.         {
  732.         *str = toupper(*str);
  733.         str++;
  734.         }
  735.     }
  736.  
  737. int match(str, pattern)
  738.     register char *str, *pattern;
  739.     {
  740.     check_ctrl_c();
  741.     while(1)
  742.         {
  743.         if(*str != *pattern) return(0);
  744.         str++;
  745.         pattern++;
  746.         if(*pattern == '\0') return(1);
  747.         if(*str == '\0') return(1);
  748.         if(*str == '=') return(1);
  749.         }
  750.     }
  751.  
  752. int num(str)
  753.     char *str;
  754.     {
  755.     register char *temp;
  756.  
  757.     check_ctrl_c();
  758.     temp = index(str, '=');
  759.     if(temp == NULL) return(0);
  760.         else return(atoi(temp + 1));
  761.     }
  762.  
  763. cleanup()
  764.     {
  765.     int  i;
  766.  
  767. /*
  768.     Unnecessary, but a good habit to get into.  Added when I was looking
  769.     for a creeping memory loss.
  770. */
  771.     i = 0;
  772.     while(i < 3)
  773.         {
  774.         if(infile [i] != NULL) fclose(infile [i]);
  775.         infile [i] = NULL;
  776.         i++;
  777.         }
  778.     if(outfile != NULL) fclose(outfile);
  779.     outfile = NULL;
  780.     /* Free up any memory that's not been freed yet.  Necessary. */
  781.     TrackFreeAll();
  782.     exit();
  783.     }
  784.  
  785. /*
  786.     Node structure for our linked list that keeps track of memory allocations.
  787.     This wouldn't be necessary if the original program wasn't so sloppy
  788.     about it's allocations and deallocations.  Note that this was added
  789.     so the program wouldn't eat memory when run on an Amiga.  (no resource
  790.     tracking, ya know!)  Also note that I couldn't use the native routines
  791.     such as AllocRemember() on the Amiga, 'cause with them it's all or nothing,
  792.     and this program selectively frees memory at odd times.
  793. */
  794.  
  795. struct TrackNode    {
  796.                     struct TrackNode *next;
  797.                     char *address;
  798.                     unsigned long size;
  799.                     };
  800.  
  801. /*
  802.     Base pointer for our linked list.  Also an end-of-list pointer so we
  803.     don't have to scan the entire list to find the end in order to add a
  804.     new node.  Saves lots of time!
  805. */
  806. static struct TrackNode *TrackBase = NULL;
  807. static struct TrackNode *TrackLEnd  = NULL;
  808.  
  809. char *TrackMalloc(msize)
  810.     unsigned long msize;
  811.     {
  812.     register struct TrackNode *temp, *current;
  813.     char *memory;
  814.  
  815.     temp = (struct TrackNode *) AllocMem(sizeof(struct TrackNode), MEMF_PUBLIC);
  816.     if(temp == NULL) return(NULL);
  817.     memory = (char *) AllocMem(msize, MEMF_PUBLIC);
  818.     if(memory == NULL)
  819.         {
  820.         FreeMem(temp, sizeof(struct TrackNode));
  821.         return(NULL);
  822.         }
  823.     if(TrackBase == NULL)
  824.         {
  825.         TrackBase = temp;
  826.         TrackBase->next = NULL;
  827.         TrackBase->address = memory;
  828.         TrackBase->size = msize;
  829.         TrackLEnd = TrackBase;
  830.         }
  831.     else
  832.         {
  833.         current = TrackLEnd;
  834.         current->next = temp;
  835.         current = current->next;
  836.         current->next = NULL;
  837.         current->address = memory;
  838.         current->size = msize;
  839.         TrackLEnd = current;
  840.         }
  841.     return(memory);
  842.     }
  843.  
  844. /*
  845.     Unfortunately, in this case we must scan the list.  This routine is
  846.     probably relatively slow, although I optimized it as much as possible.
  847. */
  848. TrackFree(pointer)
  849.     register char *pointer;
  850.     {
  851.     register struct TrackNode *current, *prev;
  852.  
  853.     current = TrackBase;
  854.     if(current == NULL)
  855.         {
  856.         return(NULL);
  857.         }
  858.     prev = TrackBase;
  859.     while(current->next != NULL && current->address != pointer)
  860.         {
  861.         prev = current;
  862.         current = current->next;
  863.         }
  864.     if(current->address != pointer)
  865.         {
  866.         return(NULL);
  867.         }
  868.     if(current == TrackBase)
  869.         {
  870.         TrackBase = current->next;
  871.         }
  872.     else
  873.         {
  874.         prev->next = current->next;
  875.         }
  876.     FreeMem(current->address, current->size);
  877.     FreeMem(current, sizeof(struct TrackNode));
  878.     if(current == TrackLEnd) TrackLEnd = prev;
  879.     return(1);
  880.     }
  881.  
  882. /*
  883.     Free up all memory that's still allocated.
  884. */
  885.  
  886. TrackFreeAll()
  887.     {
  888.     register struct TrackNode *current, *hold;
  889.  
  890.     current = TrackBase;
  891.     while(current != NULL)
  892.         {
  893.         FreeMem(current->address, current->size);
  894.         hold = current->next;
  895.         FreeMem(current, sizeof(struct TrackNode));
  896.         current = hold;
  897.         }
  898.     TrackBase = NULL;
  899.     TrackLEnd = NULL;
  900.     return(1);
  901.     }
  902.  
  903. trim(start)
  904.     char *start;
  905.     {
  906.     int  slen;
  907.     register char *end;
  908.  
  909.     slen = strlen(start);
  910.     if(slen == 0) return(0);
  911.     end = start + slen - 1;
  912.     while( (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')
  913.            && end >= start) end--;
  914.     end++;
  915.     *end = '\0';
  916.     }
  917.  
  918. ctrl_c_pressed()
  919.     {
  920.     if(SetSignal(0l, 0l) & SIGBREAKF_CTRL_C)
  921.         {
  922.         return(1);
  923.         }
  924.     SetSignal(0l, SIGBREAKF_CTRL_C);
  925.     return(NULL);
  926.     }
  927.  
  928. check_ctrl_c()
  929.     {
  930.     if(ctrl_c_pressed())
  931.         {
  932.         printf("\n\033[7m Interrupt \033[m\n\n");
  933.         cleanup();
  934.         }
  935.     }
  936.